const fs = require('fs');
const { CompilationEngine } = require('./CompilationEngine');
const { fileUtils } = require('./FileUtils');
const { JackTokenizer } = require('./JackTokenizer');
const { SymbolTable } = require('./SymbolTable');
const { VMWriter } = require('./VMWriter.js');

class JackCompiler {
  /**
   * @param {JackTokenizer} tokenizer
   * @param {CompilationEngine} compilationEngine
   * @param {VMWriter} vmWriter
   * @param {object[]} filePaths
   * @param {string} filePaths[].input
   * @param {string} filePaths[].output
   */
  constructor(tokenizer, compilationEngine, vmWriter, filePaths) {
    this.tokenizer = tokenizer;
    this.compilationEngine = compilationEngine;
    this.vmWriter = vmWriter;
    this.filePaths = filePaths;
  }

  run() {
    this.filePaths.forEach((fileConfig) => {
      this.processOneFile(fileConfig);
    });
  }

  processOneFile({ input, output }) {
    const tokens = this._getTokens(input);
    const parseTree = this._getParseTree(tokens);
    const outputCache = this._getOutputCache(parseTree);

    fs.writeFileSync(output, outputCache);
  }

  _getTokens(input) {
    this.tokenizer.reset(fs.readFileSync(input).toString());
    const tokens = [];
    while (this.tokenizer.hasMoreTokens()) {
      this.tokenizer.advance();
      tokens.push(this.tokenizer.getClonedCurrentToken());
    }
    return tokens;
  }

  _getParseTree(tokens) {
    return this.compilationEngine.generateParseTree(tokens);
  }

  _getOutputCache(parseTree) {
    this.vmWriter.setParseTree(parseTree);
    this.vmWriter.resetSymbolTable();
    return this.vmWriter.writeToCache();
  }
}

const filePaths = fileUtils.getFilePaths();
const tokenizer = new JackTokenizer();
const engine = new CompilationEngine();
const vmWriter = new VMWriter(new SymbolTable(), new SymbolTable());
const compiler = new JackCompiler(tokenizer, engine, vmWriter, filePaths);
compiler.run();
